﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Fluent;
using XmlStudio.Interfaces;
using XmlStudio.Controllers.Interfaces;
using System.Windows.Input;
using XmlStudio.DataLayer;
using System.Diagnostics;
using System.Windows;
using XmlStudio.ViewModel.Node;
using XmlStudio.Dialogs;
using Microsoft.Practices.EnterpriseLibrary.ExceptionHandling;

namespace XmlStudio.ViewModel {
    /// <summary>
    /// Wrapper class.
    /// </summary>
    public class CreateXsdElementParam {
        public XsdElement ElementType { get; set; }
        public BigXmlNode Node { get; set; }
    }

    /// <summary>
    /// Wrapper class.
    /// </summary>
    public class EditXsdElementParam {
        public XsdElement ElementType { get; set; }
        public XmlElementViewModel Element { get; set; }
        public BigXmlNode Node { get; set; }
    }


    /// <summary>
    /// Represents view model for XSD part.
    /// </summary>
    public class XsdViewModel : XmlViewModel {
        #region Fields
        private readonly IXsdModel xsdModel;

        #region Commands
        /// <summary>
        /// Creates new XSD element.
        /// </summary>
        public static readonly RoutedCommand CreateXsdElementCommand = new RoutedCommand("CreateXsdElement", typeof(XsdViewModel));

        /// <summary>
        /// Edits an element.
        /// </summary>
        public static readonly RoutedCommand EditXsdElementCommand = new RoutedCommand("EditXsdElement", typeof(XsdViewModel));

        /// <summary>
        /// Checks the XSD.
        /// </summary>
        public static readonly RoutedCommand CheckXsdCommand = new RoutedCommand("CheckXsd", typeof(XsdViewModel));
        #endregion
        #endregion

        #region Constructors
        /// <summary>
        /// Initializes a new instance of the <see cref="XsdViewModel"/> class.
        /// </summary>
        /// <param name="application">The application.</param>
        /// <param name="controller">The controller.</param>
        /// <param name="model">The model.</param>
        /// <param name="view">The view.</param>
        public XsdViewModel(App application, IController controller, IXsdModel model, IXmlView view)
            : base(application, controller, model, view) {

            this.xsdModel = model;
            this.rootContextMenu = Application.Current.TryFindResource("DesignerCanvasContextMenuXsd") as System.Windows.Controls.ContextMenu;
            this.RegisterCommands();
        }
        #endregion

        #region Properties
        /// <summary>
        /// Gets the context menu for canvas.
        /// </summary>
        /// <value></value>
        public override System.Windows.Controls.ContextMenu RootContextMenu {
            get { return this.rootContextMenu; }
        }
        #endregion

        #region Methods
        /// <summary>
        /// Unregisters commands.
        /// </summary>
        public override void UnregisterCommands() {
            base.UnregisterCommands();

            this.UnregisterCommand(CreateXsdElementCommand);
            this.UnregisterCommand(EditXsdElementCommand);
            this.UnregisterCommand(CheckXsdCommand);
        }

        /// <summary>
        /// Registers the commands.
        /// </summary>
        private void RegisterCommands() {
            #region CreateXsdElementCommand
            RegisterCommand(CreateXsdElementCommand, param => CanCreateXsdElement(param as object), param => CreateXsdElement(param as CreateXsdElementParam));
            #endregion

            #region EditXsdElementCommand
            RegisterCommand(EditXsdElementCommand, param => CanEditXsdElement(param as object), param => EditXsdElement(param as EditXsdElementParam));
            #endregion

            #region CheckXsdCommand
            RegisterCommand(CheckXsdCommand, param => CanCheckXsd(param as object), param => CheckXsd(param as object));
            #endregion
        }

        #region CheckXsdCommand
        private bool CanCheckXsd(object param) {
            return true;
        }

        private void CheckXsd(object param) {
            this.controller.CheckXsd();
        }
        #endregion

        #region EditXsdElement
        private bool CanEditXsdElement(object param) {
            return true;
        }

        private void EditXsdElement(EditXsdElementParam param) {
            try {
                var editXsdElementDialog = new EditXsdElementDialog(param.Element, param.ElementType, null, this.xsdModel);
                //PositionDialogRightDownFromMouse(editXsdElementDialog);
                var res = editXsdElementDialog.ShowDialog();

                if(res.HasValue && res.Value) {
                    this.SaveXmlElement(param.Element);
                }
            } catch(Exception ex) {
                bool rethrow = ExceptionPolicy.HandleException(ex, DefaultExceptionPolicy);
                if(rethrow) {
                    throw;
                }
            }
        }
        #endregion

        #region CreateXsdElement
        private bool CanCreateXsdElement(object param) {
            if(param == null && this.root != null) {
                return false;
            }
            
            return true;
        }

        private void CreateXsdElement(CreateXsdElementParam param) {
            try {
                if(param == null) { // create schema element
                    var element = this.controller.CreateElementSchema(null, null, null, null, null, null, null, "http://www.w3.org/2001/XMLSchema", null, null);

                    var newElement = CreateVertex(element, this.model.Mode, this.view as MainWindow) as XmlElementViewModel; //new XmlElementViewModel(element);

                    if(newElement == null) {
                        throw new InvalidOperationException("Created view model is invalid!");
                    }

                    var editXsdElementDialog = new EditXsdElementDialog(newElement, XsdElement.Schema, XsdAttributeFactory.ListRequiredAttributes(XsdElement.Schema), this.xsdModel);
                    //PositionDialogRightDownFromMouse(editXsdElementDialog);
                    var res = editXsdElementDialog.ShowDialog();
                    if(res.HasValue && res.Value) {
                        this.controller.CreateRootElement(newElement.Prefix, newElement.LocalName, newElement.NamespaceURI, newElement.Attributes);
                    }
                } else {
                    var createdElement = this.controller.CreateXsdElement(XsdElementParser.GetElementName(param.ElementType));

                    var newElement = CreateVertex(createdElement, this.model.Mode, this.view as MainWindow) as XmlElementViewModel;
                    if(newElement == null) {
                        throw new InvalidOperationException("Created view model is invalid!");
                    }

                    var editXsdElementDialog = new EditXsdElementDialog(newElement, param.ElementType, XsdAttributeFactory.ListRequiredAttributes(param.ElementType), this.xsdModel);
                    //PositionDialogRightDownFromMouse(editXsdElementDialog);
                    var res = editXsdElementDialog.ShowDialog();
                    if(res.HasValue && res.Value) {
                        this.controller.AddChildElement(param.Node, newElement.Prefix, newElement.NamespaceURI, newElement.LocalName,
                            newElement.Attributes.ToDictionary(x => x.Name), Enums.InsertMode.Last, null);
                    }
                }
            } catch(Exception ex) {
                bool rethrow = ExceptionPolicy.HandleException(ex, DefaultExceptionPolicy);
                if(rethrow) {
                    throw;
                }
            }
        }
        #endregion
        #endregion
    }
}
